package com.repair.rpc.config;

import com.alibaba.fastjson2.JSON;
import com.repair.base.exception.BizException;
import com.repair.base.exception.SystemException;
import com.repair.base.response.BaseResponse;
import com.repair.rpc.utils.FacadeResponse;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * @author chunyu
 * @since 2024/10/22,19:34
 */
@Slf4j
@Aspect
public class FacadeAspect {

    @Around("@annotation(com.repair.rpc.config.Facade)")
    public Object facade(ProceedingJoinPoint joinPoint) throws Exception {

        // 获取时间 (spring启动计时的类 在AbstractApplicationContext 中的 refresh 方法就是启动和核心方法)
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        // 获取执行的方法名
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        // 获取参数列表
        Object[] args = joinPoint.getArgs();
        log.debug("开始执行方法:method:{},args:{}", method.getName(), args);
        // 获取返回值类型
        Class<?> returnType = ((MethodSignature) joinPoint.getSignature()).getMethod().getReturnType();

        // TODO 循环遍历所有参数，进行参数校验
        try {
            // 执行具体方法 返回值就是我们的 BaseResponse
            Object response = joinPoint.proceed();
            stopWatch.stop();
            // 这里咱们的 response 有code 和message 几乎没用到 就是因为这里进行了补全
            tableObject(response);

            printLogInfo(stopWatch, method, args, "执行结束", response, null);
            return response;
        } catch (Throwable e) {
            // 失败返回 失败的response
            stopWatch.stop();
            printLogInfo(stopWatch, method, args, "方法异常", null, e);
            return getFailedResponse(returnType, e);
        }
    }

    private Object getFailedResponse(Class<?> returnType, Throwable throwable) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {

        //如果返回值的类型为BaseResponse 的子类，则创建一个通用的失败响应
        if (returnType.getDeclaredConstructor().newInstance() instanceof BaseResponse) {
            BaseResponse response = (BaseResponse) returnType.getDeclaredConstructor().newInstance();
            response.setSuccess(false);
            if (throwable instanceof BizException) {
                BizException bizException = (BizException) throwable;
                response.setResponseMessage(bizException.getErrorCode().getMessage());
                response.setResponseCode(String.valueOf(bizException.getErrorCode().getCode()));
            } else if (throwable instanceof SystemException) {
                SystemException systemException = (SystemException) throwable;
                response.setResponseMessage(systemException.getErrorCode().getMessage());
                response.setResponseCode(String.valueOf(systemException.getErrorCode().getCode()));
            } else {
                response.setResponseMessage(throwable.toString());
                response.setResponseCode(FacadeResponse.FAIL.name());
            }
            return response;
        }
        log.error(
                "返回的类型不是BaseResponse, 而是 , returnType (" + returnType);
        return null;
    }


    /**
     * 打印日志
     *
     * @param stopWatch 计时
     * @param method    方法
     * @param args      参数
     * @param action    行为 成功或异常
     * @param response  返回值
     * @param throwable 异常
     */
    private void printLogInfo(StopWatch stopWatch, Method method, Object[] args, String action, Object response, Throwable throwable) {
        log.debug(getInfoMessage(action, stopWatch, method, args, response, throwable));
    }

    /**
     * 拼接日志输出格式
     *
     * @param action    行为 成功或异常
     * @param stopWatch 计时
     * @param method    方法
     * @param args      参数
     * @param response  返回值
     * @param throwable 异常
     * @return 日志信息
     */
    private String getInfoMessage(String action, StopWatch stopWatch, Method method, Object[] args,Object response, Throwable throwable) {
        StringBuilder sb = new StringBuilder(action);
        sb.append(" ,method = ");
        sb.append(method.getName());
        sb.append(" ,time = ");
        sb.append(stopWatch.getTotalTimeMillis()).append(" ms");
        if (response instanceof BaseResponse) {
            sb.append(" ,success = ");
            sb.append(((BaseResponse) response).getSuccess());
        }
        if (throwable != null) {
            sb.append(" ,success = ");
            sb.append(false);
        }
        sb.append(" ,args = ");
        sb.append(JSON.toJSONString(Arrays.toString(args)));

        if (response != null) {
            sb.append(" ,response = ");
            sb.append(JSON.toJSONString(response));
        }

        if (throwable != null) {
            sb.append(" ,exception = ");
            sb.append(throwable.getMessage());
        }

        if (response instanceof BaseResponse) {
            BaseResponse baseResponse = (BaseResponse) response;
            if (!baseResponse.getSuccess()) {
                sb.append(" , 执行失败");
            }
        }

        return sb.toString();
    }

    /**
     * 对 response 的 code 和 message 进行补全
     *
     * @param response 统一返回值 BaseResponse
     */
    private void tableObject(Object response) {
        if (response instanceof BaseResponse) {
            if (!((BaseResponse) response).getSuccess()) {
                if (StringUtils.hasText(((BaseResponse) response).getResponseCode())) {
                    // 如果是false 设置code 和message
                    ((BaseResponse) response).setResponseCode(FacadeResponse.FAIL.getCode());
                    ((BaseResponse) response).setResponseMessage(FacadeResponse.FAIL.getMessage());
                }
            } else {
                if (!StringUtils.hasText(((BaseResponse) response).getResponseCode())) {
                    //如果是true
                    ((BaseResponse) response).setResponseCode(FacadeResponse.SUCCESS.getCode());
                    ((BaseResponse) response).setResponseMessage(FacadeResponse.SUCCESS.getMessage());
                }
            }
        }
    }

}

